매일 데이터를 분석하는 현업작업에서 단일 데이터프레임을 갖고 작업하는 경우는 매우 드물다. 보통 데이터 통계분석에는 수많은 테이블이 동원된다. 이런 경우 여러 형태를 갖는 테이블을 자유로이 다룰 수 있는 능력도 필수적이다. dplyr에는 두 테이블 동사로 다음과 같은 세가지 유형을 지원한다.
Base R에는 동일한 개념이 필요하여 merge() 함수로 기능을 제공하였지만, dplyr 두-테이블 동사를 활용해서도 동일한 기능을 구현하는데 문제가 없다.
merge와 dplyr 두-테이블 동사
기존 Base R에서 많이 사용된 merge 함수와 비교하여 dplyr 두-테이블 동사를 사용하는 경우 장점은 다음과 같다.
테이블 두개를 결합할 경우 생각보다 다양한 조합의 수가 존재한다. 두 테이블을 병합할 때, 먼저 두 테이블 모두 공통된 변수가 존재해야 된다. 이를 키라고 부르는데, 두 테이블 모두 존재하기 때문에 명칭을 달리할 필요가 있다. 따라서, 기준 테이블의 키를 기본키(Primary key), 병합할 테이블의 키를 외래키(Foreign key)라고 부른다. 기본키와 외래키를 꼭 한 필드로 고정할 필요는 없고, 다수 필드를 묶어 기본키와 외래키를 정의하는 것도 가능하다.
mutate join 유형 병합(join)데이블 두개를 병합하는데 사용되는 기본 병합(join) 동사는 4개가 있다: left_join(), right_join(), inner_join(), full_join() 이런 유형의 두 데이터프레임 동사는 데이터프레임의 형태가 변형(mutate)된다는 특징이 있어 이를 mutate join 동사라고 한다.
먼저 superheroes, publishers 데이터프레임을 생성한다.
library(tidyverse)
library(readr)
superheroes <- "
name, alignment, gender, publisher
Magneto, bad, male, Marvel
Storm, good, female, Marvel
Mystique, bad, female, Marvel
Batman, good, male, DC
Joker, bad, male, DC
Catwoman, bad, female, DC
Hellboy, good, male, Dark Horse Comics
"
superheroes <- read_csv(superheroes, trim_ws = TRUE, skip = 1)
publishers <- "
publisher, yr_founded
DC, 1934
Marvel, 1939
Image, 1992
"
publishers <- read_csv(publishers, trim_ws = TRUE, skip = 1)inner_join()inner_join()은 두 테이블 모두에 존재하는 행이 있는 경우 이를 병합하여 새로운 테이블을 생성시킨다.
inner_join(x,y) 를 수행하면 기준 테이블의 모든 행을 반환시키는데 조건은 상대 테이블에 매칭되는 행이 있는 경우다. 두 테이블 사이 다수가 매칭되면, 모든 매칭 조합이 반환된다. 대표적인 mutating join 사례다.
superheroes_kable <- knitr::kable(superheroes)
publishers_kable <- knitr::kable(publishers)
ijsp_kable <- knitr::kable(ijsp)|
superheroes
|
publishers
|
semi-join(x = superheroes, y = publishers)
|
left_join()# A tibble: 7 x 5
name alignment gender publisher yr_founded
<chr> <chr> <chr> <chr> <dbl>
1 Magneto bad male Marvel 1939
2 Storm good female Marvel 1939
3 Mystique bad female Marvel 1939
4 Batman good male DC 1934
5 Joker bad male DC 1934
6 Catwoman bad female DC 1934
7 Hellboy good male Dark Horse Comics NA
|
superheroes
|
publishers
|
left_join(x = superheroes, y = publishers)
|
right_join()# A tibble: 7 x 5
name alignment gender publisher yr_founded
<chr> <chr> <chr> <chr> <dbl>
1 Batman good male DC 1934
2 Joker bad male DC 1934
3 Catwoman bad female DC 1934
4 Magneto bad male Marvel 1939
5 Storm good female Marvel 1939
6 Mystique bad female Marvel 1939
7 <NA> <NA> <NA> Image 1992
|
superheroes
|
publishers
|
right_join(x = superheroes, y = publishers)
|
full_join()# A tibble: 8 x 5
name alignment gender publisher yr_founded
<chr> <chr> <chr> <chr> <dbl>
1 Magneto bad male Marvel 1939
2 Storm good female Marvel 1939
3 Mystique bad female Marvel 1939
4 Batman good male DC 1934
5 Joker bad male DC 1934
6 Catwoman bad female DC 1934
7 Hellboy good male Dark Horse Comics NA
8 <NA> <NA> <NA> Image 1992
|
superheroes
|
publishers
|
full_join(x = superheroes, y = publishers)
|
filtering join 유형 병합(join)mutate join과 달리 filtering join은 해당 데이터프레임에 새로운 것이 추가되거나 해서 변형되는 것이 아니라 다른 데이터프레임을 참조해서 필터링된다는 점에서 차이가 난다.
semi_join()semi_join()은 두 데이터프레임에 공통된 것을 필터로 찾아서 반환시키는 역할을 수행한다.
# A tibble: 6 x 4
name alignment gender publisher
<chr> <chr> <chr> <chr>
1 Magneto bad male Marvel
2 Storm good female Marvel
3 Mystique bad female Marvel
4 Batman good male DC
5 Joker bad male DC
6 Catwoman bad female DC
|
superheroes
|
publishers
|
semi-join(x = superheroes, y = publishers)
|
anti_join()anti_join()은 두 데이터프레임에 공통된 것을 필터로 찾아서 semi_join()과 정반대 역할을 수행한다. 즉, anti_join(x, y)은 필터링 조인으로 y 데이터프레임에 매칭되지 않지만 x 데이터프레임에 있는 칼럼만 반환한다.
# A tibble: 1 x 2
publisher yr_founded
<chr> <dbl>
1 Image 1992
|
publishers
|
superheroes
|
anti_join(x = publishers, y = superheroes)
|
| R | SQL |
|---|---|
| inner_join() | SELECT * FROM x JOIN y ON x.a = y.a |
| left_join() | SELECT * FROM x LEFT JOIN y ON x.a = y.a |
| right_join() | SELECT * FROM x RIGHT JOIN y ON x.a = y.a |
| full_join() | SELECT * FROM x FULL JOIN y ON x.a = y.a |
| semi_join() | SELECT * FROM x WHERE EXISTS (SELECT 1 FROM y WHERE x.a = y.a) |
| anti_join() | SELECT * FROM x WHERE NOT EXISTS (SELECT 1 FROM y WHERE x.a = y.a) |
| intersect(x,y) | SELECT * FROM x INTERSECT SELECT * FROM y |
| union(x, y) | SELECT * FROM x UNION SELECT * FROM y |
| setdiff(x, y) | SELECT * FROM x EXCEPT SELECT * FROM y |